package dubbo.learn.core.component.reference;

import dubbo.learn.common.RpcContext;
import dubbo.learn.common.contract.registry.RegistryLayerRpcInvoker;
import dubbo.learn.common.spi.SpiServiceLoader;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * <dubbo:reference>元素的核心数据结构
 */
@Data
@Slf4j
public class RemoteServiceReferenceFactory implements FactoryBean,
        BeanFactoryAware, InvocationHandler {

    BeanFactory beanFactory;

    /**
     * id
     */
    private String id;

    /**
     * 远端服务的全名
     */
    private String remoteInterfaceName;

    private Class<?> remoteInterfaceNameClazz;




    @Override
    public Object getObject() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        return Proxy.newProxyInstance(classLoader,
                new Class[]{remoteInterfaceNameClazz},this);
    }

    @Override
    public Class<?> getObjectType() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try {
            Class<?> clazz = classLoader.loadClass(remoteInterfaceName);
            remoteInterfaceNameClazz = clazz;
            return clazz;
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("illegal remoteInterfaceName");
        }

    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        RegistryLayerRpcInvoker registryLayerRpcInvoker =
                SpiServiceLoader.loadService(RegistryLayerRpcInvoker.class);

        RpcContext rpcContext = new RpcContext();
        rpcContext.setProxy(proxy);
        rpcContext.setMethod(method);
        rpcContext.setArgs(args);
        rpcContext.setServiceName(method.getDeclaringClass().getName());

        Object o = registryLayerRpcInvoker.invoke(rpcContext);
        return o;
    }



    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}
